package com.nf.health.app.customview;


import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;

import java.util.LinkedList;
import java.util.Queue;

public class HorizontalListView extends AdapterView<ListAdapter> {  

  public boolean mAlwaysOverrideTouch = true;  
  protected ListAdapter mAdapter;  
  private int mLeftViewIndex = -1;  
  private int mRightViewIndex = 0;  
  protected int mCurrentX;  
  protected int mNextX;  
  private int mMaxX = Integer.MAX_VALUE;  
  private int mDisplayOffset = 0;  
  protected Scroller mScroller;  
  private GestureDetector mGesture;  
  private Queue<View> mRemovedViewQueue = new LinkedList<View>();  
  private OnItemSelectedListener mOnItemSelected;  
  private OnItemClickListener mOnItemClicked;  
  private OnItemLongClickListener mOnItemLongClicked;  
  private boolean mDataChanged = false;  
    

  public HorizontalListView(Context context, AttributeSet attrs) {  
      super(context, attrs);  
      initView();  
  }  
    
  private synchronized void initView() {  
      mLeftViewIndex = -1;  
      mRightViewIndex = 0;  
      mDisplayOffset = 0;  
      mCurrentX = 0;  
      mNextX = 0;  
      mMaxX = Integer.MAX_VALUE;  
      mScroller = new Scroller(getContext());  
      mGesture = new GestureDetector(getContext(), mOnGesture);  
  }  
    
  @Override  
  public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {  
      mOnItemSelected = listener;  
  }  
    
  @Override  
  public void setOnItemClickListener(AdapterView.OnItemClickListener listener){  
      mOnItemClicked = listener;  
  }  
    
  @Override  
  public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {  
      mOnItemLongClicked = listener;  
  }  

  private DataSetObserver mDataObserver = new DataSetObserver() {  

      @Override  
      public void onChanged() {  
          synchronized(HorizontalListView.this){  
              mDataChanged = true;  
          }  
          invalidate();  
          requestLayout();  
      }  

      @Override  
      public void onInvalidated() {  
          reset();  
          invalidate();  
          requestLayout();  
      }  
        
  };  

  @Override  
  public ListAdapter getAdapter() {  
      return mAdapter;  
  }  

  @Override  
  public View getSelectedView() {  
      //TODO: implement  
      return null;  
  }  

  @Override  
  public void setAdapter(ListAdapter adapter) {  
      if(mAdapter != null) {  
          mAdapter.unregisterDataSetObserver(mDataObserver);  
      }  
      mAdapter = adapter;  
      mAdapter.registerDataSetObserver(mDataObserver);  
      reset();  
  }  
    
  private synchronized void reset(){  
      initView();  
      removeAllViewsInLayout();  
      requestLayout();  
  }  

  @Override  
  public void setSelection(int position) {  
      //TODO: implement  
  }  
    
  private void addAndMeasureChild(final View child, int viewPos) {  
      LayoutParams params = child.getLayoutParams();  
      if(params == null) {  
          params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);  
      }  

      addViewInLayout(child, viewPos, params, true);  
      child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),  
              MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));  
  }  
    
    

  @Override  
  protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {  
      super.onLayout(changed, left, top, right, bottom);  

      if(mAdapter == null){  
          return;  
      }  
        
      if(mDataChanged){  
          int oldCurrentX = mCurrentX;  
          initView();  
          removeAllViewsInLayout();  
          mNextX = oldCurrentX;  
          mDataChanged = false;  
      }  

      if(mScroller.computeScrollOffset()){  
          int scrollx = mScroller.getCurrX();  
          mNextX = scrollx;  
      }  
        
      if(mNextX <= 0){  
          mNextX = 0;  
          mScroller.forceFinished(true);  
      }  
      if(mNextX >= mMaxX) {  
          mNextX = mMaxX;  
          mScroller.forceFinished(true);  
      }  
        
      int dx = mCurrentX - mNextX;  
        
      removeNonVisibleItems(dx);  
      fillList(dx);  
      positionItems(dx);  
        
      mCurrentX = mNextX;  
        
      if(!mScroller.isFinished()){  
          post(new Runnable(){  
              @Override  
              public void run() {  
                  requestLayout();  
              }  
          });  
            
      }  
  }  
    
  private void fillList(final int dx) {  
      int edge = 0;  
      View child = getChildAt(getChildCount()-1);  
      if(child != null) {  
          edge = child.getRight();  
      }  
      fillListRight(edge, dx);  
        
      edge = 0;  
      child = getChildAt(0);  
      if(child != null) {  
          edge = child.getLeft();  
      }  
      fillListLeft(edge, dx);  
        
        
  }  
    
  private void fillListRight(int rightEdge, final int dx) {  
      while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {  
            
          View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);  
          addAndMeasureChild(child, -1);  
          rightEdge += child.getMeasuredWidth();  
            
          if(mRightViewIndex == mAdapter.getCount()-1) {  
              mMaxX = mCurrentX + rightEdge - getWidth();  
          }  
            
          if (mMaxX < 0) {  
              mMaxX = 0;  
          }  
          mRightViewIndex++;  
      }  
        
  }  
    
  private void fillListLeft(int leftEdge, final int dx) {  
      while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {  
          View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);  
          addAndMeasureChild(child, 0);  
          leftEdge -= child.getMeasuredWidth();  
          mLeftViewIndex--;  
          mDisplayOffset -= child.getMeasuredWidth();  
      }  
  }  
    
  private void removeNonVisibleItems(final int dx) {  
      View child = getChildAt(0);  
      while(child != null && child.getRight() + dx <= 0) {  
          mDisplayOffset += child.getMeasuredWidth();  
          mRemovedViewQueue.offer(child);  
          removeViewInLayout(child);  
          mLeftViewIndex++;  
          child = getChildAt(0);  
            
      }  
        
      child = getChildAt(getChildCount()-1);  
      while(child != null && child.getLeft() + dx >= getWidth()) {  
          mRemovedViewQueue.offer(child);  
          removeViewInLayout(child);  
          mRightViewIndex--;  
          child = getChildAt(getChildCount()-1);  
      }  
  }  
    
  private void positionItems(final int dx) {  
      if(getChildCount() > 0){  
          mDisplayOffset += dx;  
          int left = mDisplayOffset;  
          for(int i=0;i<getChildCount();i++){  
              View child = getChildAt(i);  
              int childWidth = child.getMeasuredWidth();  
              child.layout(left, 0, left + childWidth, child.getMeasuredHeight());  
              left += childWidth + child.getPaddingRight();  
          }  
      }  
  }  
    
  public synchronized void scrollTo(int x) {  
      mScroller.startScroll(mNextX, 0, x - mNextX, 0);  
      requestLayout();  
  }  
    
  @Override  
  public boolean dispatchTouchEvent(MotionEvent ev) {  
      boolean handled = super.dispatchTouchEvent(ev);  
      handled |= mGesture.onTouchEvent(ev);  
      return handled;  
  }  
    
  protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
              float velocityY) {  
      synchronized(HorizontalListView.this){  
          mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0);  
      }  
      requestLayout();  
        
      return true;  
  }  
    
  protected boolean onDown(MotionEvent e) {  
      mScroller.forceFinished(true);  
      return true;  
  }  
    
  private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {  

      @Override  
      public boolean onDown(MotionEvent e) {  
          return HorizontalListView.this.onDown(e);  
      }  

      @Override  
      public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
              float velocityY) {  
          return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);  
      }  

      @Override  
      public boolean onScroll(MotionEvent e1, MotionEvent e2,  
              float distanceX, float distanceY) {  
            
          synchronized(HorizontalListView.this){  
              mNextX += (int)distanceX;  
          }  
          requestLayout();  
            
          return true;  
      }  

      @Override  
      public boolean onSingleTapConfirmed(MotionEvent e) {  
          for(int i=0;i<getChildCount();i++){  
              View child = getChildAt(i);  
              if (isEventWithinView(e, child)) {  
                  if(mOnItemClicked != null){  
                      mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));  
                  }  
                  if(mOnItemSelected != null){  
                      mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));  
                  }  
                  break;  
              }  
                
          }  
          return true;  
      }  
        
      @Override  
      public void onLongPress(MotionEvent e) {  
          int childCount = getChildCount();  
          for (int i = 0; i < childCount; i++) {  
              View child = getChildAt(i);  
              if (isEventWithinView(e, child)) {  
                  if (mOnItemLongClicked != null) {  
                      mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));  
                  }  
                  break;  
              }  

          }  
      }  

      private boolean isEventWithinView(MotionEvent e, View child) {  
          Rect viewRect = new Rect();  
          int[] childPosition = new int[2];  
          child.getLocationOnScreen(childPosition);  
          int left = childPosition[0];  
          int right = left + child.getWidth();  
          int top = childPosition[1];  
          int bottom = top + child.getHeight();  
          viewRect.set(left, top, right, bottom);  
          return viewRect.contains((int) e.getRawX(), (int) e.getRawY());  
      }  
  };  

    

}
